package com.itheima.search.service.impl;

import com.itheima.goods.pojo.Goods;
import com.itheima.search.service.SearchService;
import entity.ElasticUtils;
import entity.PageResult;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class SearchServiceImpl implements SearchService {

    private RestHighLevelClient client;

    @Override
    public Map search(Map<String, String> searchMap) {
        //创建Rest客户端
        client = new RestHighLevelClient(
                RestClient.builder(
                        //如果是集群，则设置多个主机，注意端口是http协议的端口
                        new HttpHost("192.168.200.151", 9200, "http")
                )
        );
        Map resultMap = new HashMap();
        //1. 检查参数 keyword关键字是必传项，为空直接返回
        if (searchMap.get("keyword") == null || searchMap.get("keyword").length() == 0) {
            return resultMap;
        }
        //2. 查询和此关键字有关的所有分类和品牌列表
        //	2.1 创建ES搜索请求
        ElasticUtils<Goods> searchSku = new ElasticUtils<>(client, "sku", Goods.class);
        SearchSourceBuilder sourceBuilder1 = new SearchSourceBuilder();

        //		设置查询条件 ， 按照keyword进行分词查询 (商品标题字段)
        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("name", searchMap.get("keyword"));
        sourceBuilder1.query(matchQueryBuilder);

        //		设置聚合条件 ， 按照品牌进行聚合分桶
        sourceBuilder1.aggregation(AggregationBuilders.terms("brand").field("brandName")).size(0);

        //		设置聚合条件 , 按照分类进行聚合分桶
        sourceBuilder1.aggregation(AggregationBuilders.terms("category").field("categoryName")).size(0);

        //	2.2 解析响应结果
        Aggregations aggregations = searchSku.searchAggs(sourceBuilder1);

        //		获取品牌聚合分桶结果
        Terms brandTerms = (Terms) aggregations.asMap().get("brand");
        List<? extends Terms.Bucket> brandBuckets = brandTerms.getBuckets();
        List<String> brandList = new ArrayList<>();
        for (Terms.Bucket brandBucket : brandBuckets) {
            String key = brandBucket.getKeyAsString();
            brandList.add(key);
        }
        //		获取分类聚合分桶结果
        Terms categoryTerms = (Terms) aggregations.asMap().get("category");
        List<? extends Terms.Bucket> categoryBuckets = categoryTerms.getBuckets();
        List<String> categoryList = new ArrayList<>();
        for (Terms.Bucket categoryBucket : categoryBuckets) {
            String key = categoryBucket.getKeyAsString();
            categoryList.add(key);
        }
        //	2.3 存入到map中 key: 品牌或分类   value: 对应的字符串集合
        resultMap.put("brandList", brandList);
        resultMap.put("categoryList", categoryList);

        //3. 查询满足条件商品数据
        //	3.1 创建ES搜索请求
        SearchSourceBuilder sourceBuilder2 = new SearchSourceBuilder();
        //		设置查询条件 , 多条件通过bool布尔条件组合
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //		  must: 按照keyword进行分词查询 (商品标题字段)
        boolQueryBuilder.must(matchQueryBuilder);
        //		  filter: 如果品牌brand不为空，词条查询(品牌字段)
        if (searchMap.get("brand") != null && searchMap.get("brand").length() > 0) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("brandName", searchMap.get("brand")));
        }
        //		  filter: 如果分类category不为空，词条查询(分类字段)
        if (searchMap.get("category") != null && searchMap.get("category").length() > 0) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("categoryName", searchMap.get("category")));
        }
        //		  filter: 如果价格price不为空，按照"-"切割字符串 ，进行范围查询
        if (searchMap.get("price") != null && searchMap.get("price").length() > 0) {
            String[] prices = searchMap.get("price").split("-");
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(prices[0]).lte(prices[1]));
        }
            sourceBuilder2.query(boolQueryBuilder);
        //		设置排序条件
        //			如果sortField字符不为空，按照该字段排序,sortRule代表升序(ASC) 降序(DESC),默认升序
        if (searchMap.get("sortField") != null && searchMap.get("sortField").length() > 0) {
            if ("DESC".equals(searchMap.get("sortRule"))) {
                sourceBuilder2.sort(searchMap.get("sortField"), SortOrder.DESC);
            }
            sourceBuilder2.sort(searchMap.get("sortField"), SortOrder.ASC);
        }

        //		设置分页条件
        //			按照pageNum ,pageSize进行分页,不传默认第一页,每页10条数据
        Integer pageNum = Integer.parseInt(String.valueOf(searchMap.get("pageNum")));
        Integer pageSize = Integer.parseInt(String.valueOf(searchMap.get("pageSize")));
        if (null == pageNum || pageNum < 1){
            pageNum =1;
        }
        if (null == pageSize || pageSize < 1){
            pageSize=10;
        }
        int from = (pageNum - 1) * pageSize;
        sourceBuilder2.from(from).size(pageSize);

        //		设置高亮条件
        //			name字段 红色高亮显示
        HighlightBuilder highlight = SearchSourceBuilder.highlight();
        highlight.field("name").preTags("<font color='red'>").postTags("</font>");
        sourceBuilder2.highlighter(highlight);

        //	3.2 解析响应结果
        PageResult<Goods> pageResult = searchSku.search(sourceBuilder2);
        //		查询满足条件总数量: total
        resultMap.put("total",pageResult.getTotal());
        //		查询一共有多少页: pages
        Long pages = (pageResult.getTotal()+pageSize-1)/pageSize;
        resultMap.put("pages",pages);
        //		查询当前页商品集合: goodList
        resultMap.put("goodList",pageResult.getRows());
        //		每页条数和第几页
        resultMap.put("pageSize",pageSize);
        resultMap.put("pageNum",pageNum);

        //4. 封装map返回
        return resultMap;
    }


}
